﻿using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

using UnityEngine.AI;
using UnityEngine.UI;

namespace Formation
{

    public abstract class FormationBase
    {

        private const float oldSlotInclinationConstant = 0.9f;

        protected float arriveDistance;
        protected float arriveRatio;
        protected const float navMeshSampleRadius = 20f;
        protected const int navMeshAreaMask = NavMesh.AllAreas;
        



        /// <summary>
        /// 判定单位到达阵型槽的距离，越短表示单位的站位越精准
        /// </summary>
        public float ArriveDistance
        {
            get { return arriveDistance; }
            set { arriveDistance = value; }
        }

        /// <summary>
        /// 单位中到达阵型槽的比率，每次UpdateSlotList时更新一次，由子类实现其更新
        /// </summary>
        public float ArriveRatio
        {
            get { return arriveRatio; }
        }

        /// <summary>
        /// unitList通过拆箱装箱来实现，使用时请考虑性能
        /// </summary>
        protected List<FormationUnit> unitList
        {
            get
            {
                List<FormationUnit> ul = new List<FormationUnit>();

                foreach(KeyValuePair<FormationSlot,FormationUnit> pair in unitSlotList )
                {
                    if(pair.Value!=null)
                    {
                        ul.Add(pair.Value);
                    }
                }
                return ul;
            }
        }
        /// <summary>
        /// slotList通过拆箱装箱来实现，使用时请考虑性能
        /// </summary>
        protected List<FormationSlot> slotList
        {
            get
            {
                List<FormationSlot> sl = new List<FormationSlot>();
                foreach (KeyValuePair<FormationSlot, FormationUnit> pair in unitSlotList) { sl.Add(pair.Key); }
                    return sl;
            }
        }
        protected int slotCount
        {
            get
            {
                return unitSlotList.Count;
            }
        }


        protected Dictionary<FormationSlot, FormationUnit> unitSlotList = new Dictionary<FormationSlot, FormationUnit>();

        public Dictionary<FormationSlot, FormationUnit> UnitSlotList
        {
            get { return unitSlotList; }
        }



        /// <summary>
        /// 为阵型分配单位并进行第一次指派
        /// </summary>
        /// <param name="unitList"></param>
        public void Init(Transform pivot, List<FormationUnit> unitList)
        {
            AssignSlot(unitList, pivot);
        }

        protected struct EOA_id : IComparable<EOA_id>
        {
            public int id;
            public float EOA;

            public int CompareTo(EOA_id other)
            {
                if (this.EOA - other.EOA < 0) return -1;
                else if (this.EOA - other.EOA > 0) return 1;
                else return 0;
            }
        }


        /// <summary>
        /// 指派阵型，函数会重新生成阵型槽列表并且为单位指派阵型槽
        /// 不保证最优指派，但在效率和质量中做了平衡
        /// </summary>
        /// <param name="units"></param>
        /// <param name="pivot"></param>
        public void AssignSlot(List<FormationUnit> InUnits, Transform pivot)
        {
            List<FormationUnit> validUnits = new List<FormationUnit>();
            for (int i = 0; i < InUnits.Count; i++)
            {
                if (InUnits[i].IsValid) validUnits.Add(InUnits[i]);
            }
            AssignSlot(validUnits, pivot, GenerateSlotList(pivot, validUnits));
        }

        /// <summary>
        /// 根据已有的阵型槽列表指派阵型槽，会先检查阵型槽列表有效性
        /// </summary>
        /// <param name="units"></param>
        /// <param name="pivot"></param>
        /// <param name="slotList"></param>
        public void AssignSlot(List<FormationUnit> InUnits, Transform pivot, List<FormationSlot> InslotList)
        {
            var  newUnitSlotList = new Dictionary<FormationSlot,FormationUnit>();
            for (int i = 0; i < InslotList.Count; i++)
            {
                newUnitSlotList.Add(InslotList[i],null);
            }
            List<FormationUnit> validUnits = new List<FormationUnit>();
            for (int i = 0; i < InUnits.Count; i++)
            {
                if (InUnits[i].IsValid) validUnits.Add(InUnits[i]);
            }
            List<bool> isOccupied = new List<bool>(InslotList.Count);
            EOA_id[] EOA = new EOA_id[validUnits.Count];//EOA=Ease Of Assignment 
            float[,] mat_cost = GetFormationCostMat(validUnits, InslotList);

            for (int i = 0; i < InslotList.Count; i++)
            {
                isOccupied.Add(false);
            }

            EOA = GetEOA(mat_cost);
            Array.Sort(EOA);
            for (int i = 0; i < validUnits.Count; i++)
            {
                int unitId = EOA[i].id;
                int nearestSlot_id = -1;
                for (int j = 0; j < InslotList.Count; j++)
                {
                    if (!isOccupied[j] && (nearestSlot_id == -1 || mat_cost[unitId, j] < mat_cost[unitId, nearestSlot_id]))
                    {
                        nearestSlot_id = j;
                    }
                }

                isOccupied[nearestSlot_id] = true;
                newUnitSlotList[InslotList[nearestSlot_id]] = validUnits[unitId];
                validUnits[unitId].unit.desireTarget = InslotList[nearestSlot_id].position;
            }
            unitSlotList = newUnitSlotList;
        }


        /// <summary>
        /// 生成阵型的距离矩阵
        /// </summary>
        /// <param name="units"></param>
        /// <param name="generatSlots"></param>
        /// <returns></returns>
        protected float[,] GetFormationCostMat(List<FormationUnit> units, List<FormationSlot> generatSlots)
        {

            float[,] mat_cost = new float[units.Count, generatSlots.Count];
            for (int i = 0; i < units.Count; i++)
            {
                int oldId = -1;
                foreach(var oldFu in unitSlotList)
                {
                    if(oldFu.Value==units[i])
                    {
                        oldId = oldFu.Key.id;
                    }
                }

                for (int j = 0; j < generatSlots.Count; j++)
                {
                    mat_cost[i, j] = FormationDistance(units[i], generatSlots[j]);
                    if(oldId==generatSlots[j].id)
                    {
                        mat_cost[i, j] *= oldSlotInclinationConstant; //在空间距离之外，单位倾向于保持与原本阵型位置一样的位置
                    }
                }
            }
            return mat_cost;
        }

        /// <summary>
        /// 生成EOA(East of Assignment)
        /// </summary>
        /// <param name="mat_cost"></param>
        /// <returns></returns>
        protected EOA_id[] GetEOA(float[,] mat_cost)
        {
            EOA_id[] EOA = new EOA_id[mat_cost.GetLength(0)];
            for (int i = 0; i < mat_cost.GetLength(0); i++)
            {
                EOA[i].id = i;
                EOA[i].EOA = 0;
                for (int j = 0; j < mat_cost.GetLength(1); j++)
                {
                    EOA[i].EOA += (1.0f / (1 + mat_cost[i, j]));
                }
            }
            return EOA;
        }


        /// <summary>
        /// 抽象化的单位与slot的距离，不仅包含到达位点的难度，还包含单位到位点的适配
        /// </summary>
        /// <param name="unit"></param>
        /// <param name="slot"></param>
        /// <returns></returns>
        public virtual float FormationDistance(FormationUnit unit, FormationSlot slot)
        {
            return unit.unit.transform.position.SqrDistance(slot.position);
        }




        public virtual void AddUnit(FormationUnit unit)
        {
            //if(unitList.Contains(unit))
            //{
            //    Debug.LogWarning("阵型错误：单位已经存在于该阵型中");
            //    return;
            //}
            //unitList.Add(unit);
            //if(unitSlotList.Count<unitSlotList.Count)
            //{

            //}

        }

        /// <summary>
        /// 根据阵型的参数生成阵型槽列表，但是不会取代本阵型的阵型槽列表
        /// </summary>
        /// <param name="pivot"></param>
        /// <param name="count"></param>
        /// <returns></returns>
        public abstract List<FormationSlot> GenerateSlotList(Transform pivot, List<FormationUnit> units);

        ///// <summary>
        ///// 根据锚点，更新阵型槽的位置，并且更新单位的位置
        ///// </summary>
        ///// <param name="units"></param>
        ///// <param name="pivot"></param>
        //public abstract void UpdateSlotList(Transform pivot);

        public abstract void UpdateLineFormationSlotPosAndRot(Transform pivot);
        public void UpdateUnitTarget()
        {
            foreach (var fu in unitSlotList)
            {
                if (fu.Value.unit == null) continue;
                fu.Value.unit.desireTarget = fu.Key.position;
                fu.Value.unit.desireRotation = fu.Key.rotation;
            }
        }
        public void UpdateArriveRatio()
        {
            int arriveCount = 0;
            int unitCount = 0;
            foreach (var fu in unitSlotList)
            {
                if (fu.Value.unit == null) continue;
                unitCount++;
                if (fu.Key.position.DistanceLessThan(fu.Value.unit.transform.position, arriveDistance))
                    arriveCount++;
            }
            arriveRatio = (float)arriveCount / unitCount;
        }




    }
}
